/*
 * Copyright (c) 2024-2025 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef TASK_H
#define TASK_H

#include <plat/config.h>
#include <arch/context.h>
#include <core/signal_common.h>
#include <core/timer.h>
#include <core/domain.h>
#include <core/domain.h>
#include <core/addrspace.h>
#include <core/endpoint.h>
#include <core/object.h>
#include <tools/list.h>
#include <stdlib/string.h>
#include <caps/capability.h>
#include <uapi/ipcbuffer.h>
#include <tools/nolibc.h>
#include <core/resource.h>

#define IPI_TASK_EMPTY ((tcb_t *)0)

#define TTYPE_USER   (0x00U)
#define TTYPE_KERNEL 0xff

#define TCB_MAGIC (0x6D746362U)

#define TASK_RUNNING       (0x0000)
#define TASK_READY         (0x0001)
#define TASK_BLOCKED       (0x0002)
#define TASK_DEAD          (0x0004)
#define TASK_SLEEP         (0x0010)
#define TASK_SLEEP_BLOCKED ((TASK_SLEEP | TASK_BLOCKED))

#define TASK_ACT_CALL (0x0010)

#define TASK_INACTIVE    (0x8001)
#define TASK_IDLE        (0x8002)
#define TASK_EXIT_ZOMBIE (0x8004)
#define TASK_DIED        (0x8008)

#define TASK_FBIT_REPORT (0x0001)
#define TASK_FBIT_CALL   (0x0002)

#define AFFINITY_CPU0    (1UL << 0)
#define AFFINITY_CPU1    (1UL << 1)
#define AFFINITY_CPU2    (1UL << 2)
#define AFFINITY_CPU3    (1UL << 3)
#define AFFINITY_CPU_ALL (AFFINITY_CPU0 | AFFINITY_CPU1 | AFFINITY_CPU2 | AFFINITY_CPU3)

#define PTHREAD_KEYS_MAX          1024
#define PTHREAD_KEY_2NDLEVEL_SIZE 32
#define PTHREAD_KEY_1STLEVEL_SIZE ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) / PTHREAD_KEY_2NDLEVEL_SIZE)

#define SCHED_FIFO     1 /* FIFO scheduling policy */
#define SCHED_RR       2 /* RR scheduling policy */
#define SCHED_SPORADIC 3 /* SPORADIC scheduling policy */

#define TASK_OCCUR_ERROR 0xFFFFFFFFFFFFFFFFUL    ///< constant representing an error state or invalid task

typedef struct timeslice_s {
    time_t       readytime;
    unsigned int timeslice;
    unsigned int timeleft;
} timeslice_t;

struct vm_area_struct {
    unsigned long vm_start;
    unsigned long vm_end;
    unsigned long heap_addr;
    unsigned long size;
    unsigned long vm_page_prot;
    unsigned long pm_start;
    unsigned long pm_end;

    char libname[64];
};
#define TASK_MAX_MMAP_COUNT 30
struct mm_struct {
    struct vm_area_struct mmap[TASK_MAX_MMAP_COUNT];
    unsigned char         mmap_index;

    unsigned long end_pm_addr;
    unsigned long mmap_base;
    unsigned long start_code, end_code, start_data, end_data;
    unsigned long start_brk, brk, start_stack;
};

struct space_struct {
    struct mm_struct mm;
    unsigned int     space_index;
    unsigned int     elf_index;
};

typedef struct replenish {
    list_t   t_sched_list;    ///< list, link to sheduler
    tcb_t   *t_task;          ///< task information
    uint64_t t_repl_time;     ///< replenishment time (us)
} replenish_t;

typedef struct sporadic_param {
    int32_t ss_low_priority;
    int32_t ss_repl_period;
    int32_t ss_init_budget;
} sporadic_param_t;

typedef struct task_control_block {
    uint32_t magic;
    uint32_t id;
    uint32_t core_id;
    uint32_t affinity;
    uint32_t type;
#ifdef CONFIG_KERNEL_MCS

#else
    timeslice_t  timeslice;
    timeslice_t *currentts;
#endif
    uint32_t state;
    uint32_t flags;

    dom_t domain;

    cptr_t cptr;

    void         *lock;
    unsigned char locktype;
    uint32_t      priority;
    int32_t       orig_priority;
    uint32_t      maxpriority;

    int32_t          ss_low_priority;
    int32_t          ss_init_budget;
    uint64_t         ss_repl_period;
    int32_t          ss_max_repl;
    int32_t          repl_count;
    uint64_t         curr_budget;
    uint64_t         activation_time;
    struct replenish replenish;

    int32_t sched_policy;

    void *cond;
    void *cond_mutex;

    void *ipcbuffer;

    struct mm_struct mm;
    capability_t     cnode;
    addrspace_t      addrspace;

    endpoint_t *faulthandler;
    int         currentfault;

    list_t      waitlist;
    list_t      sleeplist;
    list_t      runlist;
    list_t      holdlist;      // hold a linked list of resources
    list_t      mutexlist;     // a linked list of held mutexes
    resource_t *t_blockres;    // pointer to blocking resource
    resource_t  t_res;         // blocking resource

    endpoint_t *calledep;
    list_t      callernode;
    list_t      callers;

    list_t semlist;
    char   name[24];

    object_t  obj;
    cptr_t    elf_cptr;
    context_t context;

    uid_t uid, euid, suid;
    gid_t gid, egid, sgid;
#ifdef USE_ONE_STACK
    context_t *p_context;
    uint64_t   stack;
#endif
    uint32_t proc_idx;
    uint64_t stacktop;

    void **rtn;
    void  *owner;
    char   detached;

    char cancelstate;
    char canceltype;
    char canceled;

    char woken_by_cancel;
    char condvar_avail;

    void **specific[PTHREAD_KEY_1STLEVEL_SIZE];

    struct pthread_cleanup_buffer *cleanup;
    sig_cb_t                       sig_info;
} DEFAULT_ALINMENT tcb_t;

static inline bool tcb_is_valid(tcb_t *tcb)
{
    return tcb->magic == TCB_MAGIC;
}
static inline void tcb_get(tcb_t *tcb)
{
    (void)object_get(&tcb->obj);
}

static inline uint32_t task_get_state(tcb_t *task)
{
    return task->state;
}

static inline uint32_t task_get_flags(tcb_t *task)
{
    return task->flags;
}

static inline uint32_t task_get_id(tcb_t *task)
{
    return task->id;
}
static inline uint32_t task_get_affinity(tcb_t *task)
{
    return task->affinity;
}
static inline void task_set_affinity(tcb_t *task, uint32_t affinity)
{
    task->affinity = affinity;
}

static inline uint32_t task_get_coreid(tcb_t *task)
{
    return task->core_id;
}

static inline void task_set_coreid(tcb_t *task, uint32_t coreid)
{
    task->core_id = coreid;
}

static inline void task_set_state(tcb_t *tcb, uint32_t state)
{
    tcb->state = state;
}
static inline void task_set_fault(tcb_t *task, int fault)
{
    task->currentfault = fault;
}
static inline void task_set_faulthandler(tcb_t *task, endpoint_t *fh)
{
    if (task->faulthandler != NULL)
        endpoint_put(task->faulthandler);

    endpoint_get(fh);
    task->faulthandler = fh;
}

static inline void task_set_flags(tcb_t *tcb, uint32_t bits)
{
    tcb->flags |= bits;
}
static inline void task_set_addrspace(tcb_t *tcb, addrspace_t *as)
{
    tcb->addrspace.pg = as->pg;
}
static inline void task_clear_flags(tcb_t *tcb, uint32_t bits)
{
    tcb->flags &= ~bits;
}
static inline void task_set_domain(tcb_t *tcb, unsigned int domain)
{
    if (CONFIG_DOM_NUMBER <= 1) {
        domain = 0;
    }
    tcb->domain = (dom_t)domain;
}

static inline dom_t task_get_domain(tcb_t *tcb)
{
    return tcb->domain;
}
static inline ipcbuffer_t *task_get_ipcbuffer(tcb_t *tcb)
{
    return tcb->ipcbuffer;
}
static inline int task_get_fault(tcb_t *task)
{
    return task->currentfault;
}
static inline endpoint_t *task_get_faulthandler(tcb_t *task)
{
    return task->faulthandler;
}
static inline void task_set_priority(tcb_t *tcb, uint32_t prio)
{
    tcb->priority = prio;
}

static inline void task_set_sched_policy(tcb_t *task, int32_t sched_policy)
{
    task->sched_policy = sched_policy;
}

static inline void task_set_maxpriority(tcb_t *tcb, uint32_t prio)
{
    tcb->maxpriority = prio;
}
static inline unsigned int task_get_priority(tcb_t *task)
{
    return task->priority;
}
static inline void task_set_timeslice(tcb_t *tcb, unsigned int ts)
{
#ifdef CONFIG_NEW_TIME
    tcb->timeslice.timeslice = ts;
#else
    tcb->timeslice = ts;
#endif
}
static inline unsigned int task_get_maxpriority(tcb_t *task)
{
    return task->maxpriority;
}
static inline addrspace_t *task_get_addrspace(tcb_t *task)
{
    return &task->addrspace;
}
static inline context_t *task_get_context(tcb_t *task)
{
    return &task->context;
}
static inline unsigned long task_get_pc(tcb_t *task)
{
    return context_get_pc(&task->context);
}
static inline unsigned long task_get_retcode(tcb_t *task)
{
    return context_get_retcode(&task->context);
}
static inline capability_t *task_get_cnode(tcb_t *task)
{
    return &task->cnode;
}
static inline void task_set_retcode(tcb_t *tcb, uint64_t retcode)
{
    context_set_retcode(task_get_context(tcb), retcode);
}
static inline void task_set_stack(tcb_t *tcb, void *stack, void *ipcbuffer, unsigned int stacklen)
{
    tcb->ipcbuffer = ipcbuffer;
    /* PRQA S 0326 1 */
    tcb->stacktop = (uint64_t)stack + stacklen;
    context_set_stack(task_get_context(tcb), stack, stacklen);
}
static inline void task_set_entry(tcb_t *tcb, unsigned long entry)
{
    context_set_entry(task_get_context(tcb), entry);
}
static inline void task_set_reg_param(tcb_t *tcb, uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
                                      uint64_t p5, uint64_t p6, uint64_t p7)
{
    context_set_reg_param(task_get_context(tcb), p0, p1, p2, p3, p4, p5, p6, p7);
}
static inline void task_set_name(tcb_t *task, const char *name)
{
    (void)memcpy(task->name, name, sizeof(task->name));
    task->name[sizeof(task->name) - 1U] = '\0';
}
static inline void task_set_readytime(tcb_t *tcb, time_t ready)
{
    tcb->timeslice.readytime = ready;
}
static inline uint64_t task_get_readytime(const tcb_t *task)
{
    return task->timeslice.readytime.time;
}

static inline void task_prepare_exit(tcb_t *task)
{
    context_prepare_exit(task_get_context(task));
}
static inline void task_dump_info(tcb_t *task)
{
    dprintf("Task: %x, ID: %x, Name: %s, PC: %x\n", (unsigned long)task, task->id, task->name, task_get_pc(task));
}
static inline void task_set_cptr(tcb_t *tcb, cptr_t c)
{
    tcb->cptr = c;
}
static inline void task_set_elf_cptr(tcb_t *tcb, cptr_t c)
{
    tcb->elf_cptr = c;
}
void              tcb_recycle(tcb_t *tcb);
static inline int tcb_put(tcb_t *tcb)
{
    int refcount = object_put(&tcb->obj, 0, 0);
    if (refcount == 0) {
        tcb_recycle(tcb);
    }

    return refcount;
}
void task_init(tcb_t *tcb, uint32_t type);
void task_switch_prepare(tcb_t *to, tcb_t *from);
void task_clone(tcb_t *target, tcb_t *src, unsigned long id);

static inline int32_t task_get_replcount(const tcb_t *task)
{
    return task->repl_count;
}

static inline void task_set_replcount(tcb_t *task, int32_t value)
{
    task->repl_count = value;
}

static inline int32_t task_get_maxrepl(const tcb_t *task)
{
    return task->ss_max_repl;
}

static inline struct replenish *task_get_replenish(tcb_t *task)
{
    return &task->replenish;
}

static inline uint64_t task_get_replperiod(const tcb_t *task)
{
    return task->ss_repl_period;
}

static inline uint64_t task_get_initbudget(const tcb_t *task)
{
    return task->ss_init_budget;
}
static inline int32_t task_get_low_priority(const tcb_t *task)
{
    return task->ss_low_priority;
}

static inline uint64_t task_get_curbudget(const tcb_t *task)
{
    return task->curr_budget;
}

static inline void task_set_curbudget(tcb_t *task, uint64_t value)
{
    task->curr_budget = value;
}

static inline int32_t task_get_orig_priority(const tcb_t *task)
{
    return task->orig_priority;
}

static inline void task_set_orig_priority(tcb_t *task, int32_t prio)
{
    task->orig_priority = prio;
}

static inline uint64_t task_get_activation_time(const tcb_t *task)
{
    return task->activation_time;
}

static inline void task_set_activation_time(tcb_t *task, uint64_t value)
{
    task->activation_time = value;
}

static inline sig_cb_t *task_get_scb(tcb_t *task)
{
    return &task->sig_info;
}

static inline list_t *task_get_runlist(tcb_t *task)
{
    return &task->runlist;
}

static bool task_is_timeout(const tcb_t *task)
{
    if ((task_get_readytime(task) - get_current_tick() - 1ULL) > (UINT64_MAX >> 1)) {
        return true;
    }

    return false;
}

static inline uint64_t task_get_aid(const tcb_t *task)
{
    return task->addrspace.aid;
}

static inline uint32_t affinity_cpu(uint32_t m)
{
    return (uint32_t)(1UL << m);
}

uint64_t task_get_remaining_delay(const tcb_t *task);

void ipcbuffer_partial_transfer(tcb_t *dst, tcb_t *src, uint64_t tag);
void ipcbuffer_regs_transfer(tcb_t *dst, uint64_t tag, uint64_t m0, uint64_t m1);
void ipcbuffer_full_transfer(tcb_t *dst, tcb_t *src);

int task_cap_migrate(capability_t *cap, uintptr_t m0);

#endif
